#pragma once

#define _CLASS_

#include <ntdddisk.h>
#include <ntddcdrm.h>
#include <ntddtape.h>
#include <ntddscsi.h>
#include <ntddstor.h>

#include <stdio.h>

#include <scsi.h>

#define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b))

#define SRB_CLASS_FLAGS_LOW_PRIORITY      0x10000000
#define SRB_CLASS_FLAGS_PERSISTANT        0x20000000
#define SRB_CLASS_FLAGS_PAGING            0x40000000
#define SRB_CLASS_FLAGS_FREE_MDL          0x80000000

#define ASSERT_FDO(x) \
  ASSERT(((PCOMMON_DEVICE_EXTENSION) (x)->DeviceExtension)->IsFdo)

#define ASSERT_PDO(x) \
  ASSERT(!(((PCOMMON_DEVICE_EXTENSION) (x)->DeviceExtension)->IsFdo))

#define IS_CLEANUP_REQUEST(majorFunction)   \
  ((majorFunction == IRP_MJ_CLOSE) ||       \
   (majorFunction == IRP_MJ_CLEANUP) ||     \
   (majorFunction == IRP_MJ_SHUTDOWN))

#define DO_MCD(fdoExtension)                                 \
  (((fdoExtension)->MediaChangeDetectionInfo != NULL) &&     \
   ((fdoExtension)->MediaChangeDetectionInfo->MediaChangeDetectionDisableCount == 0))

#define IS_SCSIOP_READ(opCode)     \
  ((opCode == SCSIOP_READ6)   ||   \
   (opCode == SCSIOP_READ)    ||   \
   (opCode == SCSIOP_READ12)  ||   \
   (opCode == SCSIOP_READ16))

#define IS_SCSIOP_WRITE(opCode)     \
  ((opCode == SCSIOP_WRITE6)   ||   \
   (opCode == SCSIOP_WRITE)    ||   \
   (opCode == SCSIOP_WRITE12)  ||   \
   (opCode == SCSIOP_WRITE16))

#define IS_SCSIOP_READWRITE(opCode) (IS_SCSIOP_READ(opCode) || IS_SCSIOP_WRITE(opCode))

#define ADJUST_FUA_FLAG(fdoExt) {                                                       \
    if (TEST_FLAG(fdoExt->DeviceFlags, DEV_WRITE_CACHE) &&                              \
        !TEST_FLAG(fdoExt->DeviceFlags, DEV_POWER_PROTECTED) &&                         \
        !TEST_FLAG(fdoExt->ScanForSpecialFlags, CLASS_SPECIAL_FUA_NOT_SUPPORTED) ) {    \
        fdoExt->CdbForceUnitAccess = TRUE;                                              \
    } else {                                                                            \
        fdoExt->CdbForceUnitAccess = FALSE;                                             \
    }                                                                                   \
}

#define FREE_POOL(_PoolPtr)     \
    if (_PoolPtr != NULL) {     \
        ExFreePool(_PoolPtr);   \
        _PoolPtr = NULL;        \
    }

#ifdef POOL_TAGGING
#undef ExAllocatePool
#undef ExAllocatePoolWithQuota
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'nUcS')
//#define ExAllocatePool(a,b) #assert(0)
#define ExAllocatePoolWithQuota(a,b) ExAllocatePoolWithQuotaTag(a,b,'nUcS')
#endif

#define CLASS_TAG_AUTORUN_DISABLE           'ALcS'
#define CLASS_TAG_FILE_OBJECT_EXTENSION     'FLcS'
#define CLASS_TAG_MEDIA_CHANGE_DETECTION    'MLcS'
#define CLASS_TAG_MOUNT                     'mLcS'
#define CLASS_TAG_RELEASE_QUEUE             'qLcS'
#define CLASS_TAG_POWER                     'WLcS'
#define CLASS_TAG_WMI                       'wLcS'
#define CLASS_TAG_FAILURE_PREDICT           'fLcS'
#define CLASS_TAG_DEVICE_CONTROL            'OIcS'
#define CLASS_TAG_MODE_DATA                 'oLcS'
#define CLASS_TAG_MULTIPATH                 'mPcS'
#define CLASS_TAG_LOCK_TRACKING             'TLcS'
#define CLASS_TAG_LB_PROVISIONING           'PLcS'
#define CLASS_TAG_MANAGE_DATASET            'MDcS'

#define MAXIMUM_RETRIES 4

#define CLASS_DRIVER_EXTENSION_KEY ((PVOID) ClassInitialize)

#define NO_REMOVE                         0
#define REMOVE_PENDING                    1
#define REMOVE_COMPLETE                   2

#define ClassAcquireRemoveLock(devobj, tag) \
  ClassAcquireRemoveLockEx(devobj, tag, __FILE__, __LINE__)

#ifdef TRY
#undef TRY
#endif
#ifdef LEAVE
#undef LEAVE
#endif

#ifdef FINALLY
#undef FINALLY
#endif

#define TRY
#define LEAVE             goto __tryLabel;
#define FINALLY           __tryLabel:

#if defined DebugPrint
#undef DebugPrint
#endif

#if DBG
#define DebugPrint(x) ClassDebugPrint x
#else
#define DebugPrint(x)
#endif

#define DEBUG_BUFFER_LENGTH                        256

#define START_UNIT_TIMEOUT                         (60 * 4)

#define MEDIA_CHANGE_DEFAULT_TIME                  1
#define MEDIA_CHANGE_TIMEOUT_TIME                  300

#ifdef ALLOCATE_SRB_FROM_POOL

#define ClasspAllocateSrb(ext)                      \
  ExAllocatePoolWithTag(NonPagedPool,               \
                        sizeof(SCSI_REQUEST_BLOCK), \
                        'sBRS')

#define ClasspFreeSrb(ext, srb) ExFreePool((srb));

#else /* ALLOCATE_SRB_FROM_POOL */

#define ClasspAllocateSrb(ext)                      \
  ExAllocateFromNPagedLookasideList(                \
      &((ext)->CommonExtension.SrbLookasideList))

#define ClasspFreeSrb(ext, srb)                   \
  ExFreeToNPagedLookasideList(                    \
      &((ext)->CommonExtension.SrbLookasideList), \
      (srb))

#endif /* ALLOCATE_SRB_FROM_POOL */

#define SET_FLAG(Flags, Bit)    ((Flags) |= (Bit))
#define CLEAR_FLAG(Flags, Bit)  ((Flags) &= ~(Bit))
#define TEST_FLAG(Flags, Bit)   (((Flags) & (Bit)) != 0)

#define CLASS_WORKING_SET_MAXIMUM                         2048

#define CLASS_INTERPRET_SENSE_INFO2_MAXIMUM_HISTORY_COUNT 30000

#define CLASS_SPECIAL_DISABLE_SPIN_DOWN                 0x00000001
#define CLASS_SPECIAL_DISABLE_SPIN_UP                   0x00000002
#define CLASS_SPECIAL_NO_QUEUE_LOCK                     0x00000008
#define CLASS_SPECIAL_DISABLE_WRITE_CACHE               0x00000010
#define CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK         0x00000020
#if ((NTDDI_VERSION == NTDDI_WIN2KSP3) || (OSVER(NTDDI_VERSION) == NTDDI_WINXP))
#define CLASS_SPECIAL_DISABLE_WRITE_CACHE_NOT_SUPPORTED 0x00000040
#endif
#define CLASS_SPECIAL_MODIFY_CACHE_UNSUCCESSFUL         0x00000040
#define CLASS_SPECIAL_FUA_NOT_SUPPORTED                 0x00000080
#define CLASS_SPECIAL_VALID_MASK                        0x000000FB
#define CLASS_SPECIAL_RESERVED         (~CLASS_SPECIAL_VALID_MASK)

#define DEV_WRITE_CACHE                                 0x00000001
#define DEV_USE_SCSI1                                   0x00000002
#define DEV_SAFE_START_UNIT                             0x00000004
#define DEV_NO_12BYTE_CDB                               0x00000008
#define DEV_POWER_PROTECTED                             0x00000010
#define DEV_USE_16BYTE_CDB                              0x00000020

#define GUID_CLASSPNP_QUERY_REGINFOEX {0x00e34b11, 0x2444, 0x4745, {0xa5, 0x3d, 0x62, 0x01, 0x00, 0xcd, 0x82, 0xf7}}
#define GUID_CLASSPNP_SENSEINFO2      {0x509a8c5f, 0x71d7, 0x48f6, {0x82, 0x1e, 0x17, 0x3c, 0x49, 0xbf, 0x2f, 0x18}}
#define GUID_CLASSPNP_WORKING_SET     {0x105701b0, 0x9e9b, 0x47cb, {0x97, 0x80, 0x81, 0x19, 0x8a, 0xf7, 0xb5, 0x24}}
#define GUID_CLASSPNP_SRB_SUPPORT     {0x0a483941, 0xbdfd, 0x4f7b, {0xbe, 0x95, 0xce, 0xe2, 0xa2, 0x16, 0x09, 0x0c}}

#define DEFAULT_FAILURE_PREDICTION_PERIOD 60 * 60 * 1

#define MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS (0x3b9aca00)

static inline ULONG CountOfSetBitsUChar(UCHAR _X)
{
    ULONG i = 0; while (_X) { _X &= _X - 1; i++; } return i;
}
static inline ULONG CountOfSetBitsULong(ULONG _X)
{
    ULONG i = 0; while (_X) { _X &= _X - 1; i++; } return i;
}
static inline ULONG CountOfSetBitsULong32(ULONG32 _X)
{
    ULONG i = 0; while (_X) { _X &= _X - 1; i++; } return i;
}
static inline ULONG CountOfSetBitsULong64(ULONG64 _X)
{
    ULONG i = 0; while (_X) { _X &= _X - 1; i++; } return i;
}
static inline ULONG CountOfSetBitsUlongPtr(ULONG_PTR _X)
{
    ULONG i = 0; while (_X) { _X &= _X - 1; i++; } return i;
}

typedef enum _MEDIA_CHANGE_DETECTION_STATE {
    MediaUnknown,
    MediaPresent,
    MediaNotPresent,
    MediaUnavailable
} MEDIA_CHANGE_DETECTION_STATE, * PMEDIA_CHANGE_DETECTION_STATE;

typedef enum _CLASS_DEBUG_LEVEL {
    ClassDebugError = 0,
    ClassDebugWarning = 1,
    ClassDebugTrace = 2,
    ClassDebugInfo = 3,
    ClassDebugMediaLocks = 8,
    ClassDebugMCN = 9,
    ClassDebugDelayedRetry = 10,
    ClassDebugSenseInfo = 11,
    ClassDebugRemoveLock = 12,
    ClassDebugExternal4 = 13,
    ClassDebugExternal3 = 14,
    ClassDebugExternal2 = 15,
    ClassDebugExternal1 = 16
} CLASS_DEBUG_LEVEL, * PCLASS_DEBUG_LEVEL;

typedef enum {
    EventGeneration,
    DataBlockCollection
} CLASSENABLEDISABLEFUNCTION;

typedef enum {
    FailurePredictionNone = 0,
    FailurePredictionIoctl,
    FailurePredictionSmart,
    FailurePredictionSense
} FAILURE_PREDICTION_METHOD, * PFAILURE_PREDICTION_METHOD;

typedef enum {
    PowerDownDeviceInitial,
    PowerDownDeviceLocked,
    PowerDownDeviceStopped,
    PowerDownDeviceOff,
    PowerDownDeviceUnlocked
} CLASS_POWER_DOWN_STATE;

typedef enum {
    PowerDownDeviceInitial2,
    PowerDownDeviceLocked2,
    PowerDownDeviceFlushed2,
    PowerDownDeviceStopped2,
    PowerDownDeviceOff2,
    PowerDownDeviceUnlocked2
} CLASS_POWER_DOWN_STATE2;

typedef enum {
    PowerDownDeviceInitial3 = 0,
    PowerDownDeviceLocked3,
    PowerDownDeviceQuiesced3,
    PowerDownDeviceFlushed3,
    PowerDownDeviceStopped3,
    PowerDownDeviceOff3,
    PowerDownDeviceUnlocked3
} CLASS_POWER_DOWN_STATE3;

typedef enum {
    PowerUpDeviceInitial,
    PowerUpDeviceLocked,
    PowerUpDeviceOn,
    PowerUpDeviceStarted,
    PowerUpDeviceUnlocked
} CLASS_POWER_UP_STATE;

struct _CLASS_INIT_DATA;
typedef struct _CLASS_INIT_DATA CLASS_INIT_DATA, * PCLASS_INIT_DATA;

struct _CLASS_PRIVATE_FDO_DATA;
typedef struct _CLASS_PRIVATE_FDO_DATA CLASS_PRIVATE_FDO_DATA, * PCLASS_PRIVATE_FDO_DATA;

struct _CLASS_PRIVATE_PDO_DATA;
typedef struct _CLASS_PRIVATE_PDO_DATA CLASS_PRIVATE_PDO_DATA, * PCLASS_PRIVATE_PDO_DATA;

struct _CLASS_PRIVATE_COMMON_DATA;
typedef struct _CLASS_PRIVATE_COMMON_DATA CLASS_PRIVATE_COMMON_DATA, * PCLASS_PRIVATE_COMMON_DATA;

struct _MEDIA_CHANGE_DETECTION_INFO;
typedef struct _MEDIA_CHANGE_DETECTION_INFO MEDIA_CHANGE_DETECTION_INFO, * PMEDIA_CHANGE_DETECTION_INFO;

struct _DICTIONARY_HEADER;
typedef struct _DICTIONARY_HEADER DICTIONARY_HEADER, * PDICTIONARY_HEADER;

typedef struct _DICTIONARY {
    ULONGLONG Signature;
    struct _DICTIONARY_HEADER* List;
    KSPIN_LOCK SpinLock;
} DICTIONARY, * PDICTIONARY;

typedef struct _CLASSPNP_SCAN_FOR_SPECIAL_INFO {
    PCHAR VendorId;
    PCHAR ProductId;
    PCHAR ProductRevision;
    ULONG_PTR Data;
} CLASSPNP_SCAN_FOR_SPECIAL_INFO, * PCLASSPNP_SCAN_FOR_SPECIAL_INFO;

_IRQL_requires_max_(DISPATCH_LEVEL)
typedef VOID
(NTAPI* PCLASS_ERROR)(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PSCSI_REQUEST_BLOCK Srb,
    _Out_ NTSTATUS* Status,
    _Inout_ BOOLEAN* Retry);

_IRQL_requires_max_(PASSIVE_LEVEL)
typedef NTSTATUS
(NTAPI* PCLASS_ADD_DEVICE)(
    _In_ PDRIVER_OBJECT DriverObject,
    _In_ PDEVICE_OBJECT Pdo);

typedef NTSTATUS
(NTAPI* PCLASS_POWER_DEVICE)(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp);

_IRQL_requires_max_(PASSIVE_LEVEL)
typedef NTSTATUS
(NTAPI* PCLASS_START_DEVICE)(
    _In_ PDEVICE_OBJECT DeviceObject);

_IRQL_requires_max_(PASSIVE_LEVEL)
typedef NTSTATUS
(NTAPI* PCLASS_STOP_DEVICE)(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ UCHAR Type);

_IRQL_requires_max_(PASSIVE_LEVEL)
typedef NTSTATUS
(NTAPI* PCLASS_INIT_DEVICE)(
    _In_ PDEVICE_OBJECT DeviceObject);

_IRQL_requires_max_(PASSIVE_LEVEL)
typedef NTSTATUS
(NTAPI* PCLASS_ENUM_DEVICE)(
    _In_ PDEVICE_OBJECT DeviceObject);

_IRQL_requires_max_(DISPATCH_LEVEL)
typedef NTSTATUS
(NTAPI* PCLASS_READ_WRITE)(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp);

_IRQL_requires_max_(DISPATCH_LEVEL)
typedef NTSTATUS
(NTAPI* PCLASS_DEVICE_CONTROL)(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp);

_IRQL_requires_max_(DISPATCH_LEVEL)
typedef NTSTATUS
(NTAPI* PCLASS_SHUTDOWN_FLUSH)(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp);

_IRQL_requires_max_(PASSIVE_LEVEL)
typedef NTSTATUS
(NTAPI* PCLASS_CREATE_CLOSE)(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp);

_IRQL_requires_max_(PASSIVE_LEVEL)
typedef NTSTATUS
(NTAPI* PCLASS_QUERY_ID)(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ BUS_QUERY_ID_TYPE IdType,
    _In_ PUNICODE_STRING IdString);

_IRQL_requires_max_(PASSIVE_LEVEL)
typedef NTSTATUS
(NTAPI* PCLASS_REMOVE_DEVICE)(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ UCHAR Type);

_IRQL_requires_max_(PASSIVE_LEVEL)
typedef VOID
(NTAPI* PCLASS_UNLOAD)(
    _In_ PDRIVER_OBJECT DriverObject);

_IRQL_requires_max_(PASSIVE_LEVEL)
typedef NTSTATUS
(NTAPI* PCLASS_QUERY_PNP_CAPABILITIES)(
    _In_ PDEVICE_OBJECT PhysicalDeviceObject,
    _In_ PDEVICE_CAPABILITIES Capabilities);

_IRQL_requires_(DISPATCH_LEVEL)
typedef VOID
(NTAPI* PCLASS_TICK)(
    _In_ PDEVICE_OBJECT DeviceObject);

_IRQL_requires_max_(PASSIVE_LEVEL)
typedef NTSTATUS
(NTAPI* PCLASS_QUERY_WMI_REGINFO_EX)(
    _In_ PDEVICE_OBJECT DeviceObject,
    _Out_ ULONG* RegFlags,
    _Out_ PUNICODE_STRING Name,
    _Out_ PUNICODE_STRING MofResourceName);

_IRQL_requires_max_(PASSIVE_LEVEL)
typedef NTSTATUS
(NTAPI* PCLASS_QUERY_WMI_REGINFO)(
    _In_ PDEVICE_OBJECT DeviceObject,
    _Out_ ULONG* RegFlags,
    _Out_ PUNICODE_STRING Name);

_IRQL_requires_max_(PASSIVE_LEVEL)
typedef NTSTATUS
(NTAPI* PCLASS_QUERY_WMI_DATABLOCK)(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp,
    _In_ ULONG GuidIndex,
    _In_ ULONG BufferAvail,
    _Out_writes_bytes_(BufferAvail) PUCHAR Buffer);

_IRQL_requires_max_(PASSIVE_LEVEL)
typedef NTSTATUS
(NTAPI* PCLASS_SET_WMI_DATABLOCK)(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp,
    _In_ ULONG GuidIndex,
    _In_ ULONG BufferSize,
    _In_reads_bytes_(BufferSize) PUCHAR Buffer);

_IRQL_requires_max_(PASSIVE_LEVEL)
typedef NTSTATUS
(NTAPI* PCLASS_SET_WMI_DATAITEM)(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp,
    _In_ ULONG GuidIndex,
    _In_ ULONG DataItemId,
    _In_ ULONG BufferSize,
    _In_reads_bytes_(BufferSize) PUCHAR Buffer);

_IRQL_requires_max_(PASSIVE_LEVEL)
typedef NTSTATUS
(NTAPI* PCLASS_EXECUTE_WMI_METHOD)(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp,
    _In_ ULONG GuidIndex,
    _In_ ULONG MethodId,
    _In_ ULONG InBufferSize,
    _In_ ULONG OutBufferSize,
    _In_reads_(_Inexpressible_(max(InBufferSize, OutBufferSize))) PUCHAR Buffer);

_IRQL_requires_max_(PASSIVE_LEVEL)
typedef NTSTATUS
(NTAPI* PCLASS_WMI_FUNCTION_CONTROL)(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp,
    _In_ ULONG GuidIndex,
    _In_ CLASSENABLEDISABLEFUNCTION Function,
    _In_ BOOLEAN Enable);

typedef struct _SRB_HISTORY_ITEM {
    LARGE_INTEGER TickCountSent;
    LARGE_INTEGER TickCountCompleted;
    ULONG MillisecondsDelayOnRetry;
    SENSE_DATA NormalizedSenseData;
    UCHAR SrbStatus;
    UCHAR ClassDriverUse;
} SRB_HISTORY_ITEM, * PSRB_HISTORY_ITEM;

typedef struct _SRB_HISTORY {
    ULONG_PTR ClassDriverUse[4];
    _Field_range_(1, 30000) ULONG TotalHistoryCount;
    _Field_range_(0, TotalHistoryCount) ULONG UsedHistoryCount;
    _Field_size_part_(TotalHistoryCount, UsedHistoryCount) SRB_HISTORY_ITEM History[1];
} SRB_HISTORY, * PSRB_HISTORY;

_IRQL_requires_max_(DISPATCH_LEVEL)
typedef BOOLEAN
(NTAPI* PCLASS_INTERPRET_SENSE_INFO)(
    _In_ PDEVICE_OBJECT Fdo,
    _In_opt_ PIRP OriginalRequest,
    _In_ PSCSI_REQUEST_BLOCK Srb,
    _In_ UCHAR MajorFunctionCode,
    _In_ ULONG IoDeviceCode,
    _In_ ULONG PreviousRetryCount,
    _In_opt_ SRB_HISTORY* RequestHistory,
    _Out_ NTSTATUS* Status,
    _Out_ _Deref_out_range_(0, MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
    LONGLONG* RetryIn100nsUnits);

_IRQL_requires_max_(DISPATCH_LEVEL)
_At_(RequestHistory->UsedHistoryCount, _Pre_equal_to_(RequestHistory->TotalHistoryCount)
    _Out_range_(0, RequestHistory->TotalHistoryCount - 1))
    typedef VOID
    (NTAPI* PCLASS_COMPRESS_RETRY_HISTORY_DATA)(
        _In_ PDEVICE_OBJECT DeviceObject,
        _Inout_ PSRB_HISTORY RequestHistory);

typedef struct {
    GUID Guid;
    ULONG InstanceCount;
    ULONG Flags;
} GUIDREGINFO, * PGUIDREGINFO;

typedef struct _CLASS_WMI_INFO {
    ULONG GuidCount;
    PGUIDREGINFO GuidRegInfo;
    PCLASS_QUERY_WMI_REGINFO ClassQueryWmiRegInfo;
    PCLASS_QUERY_WMI_DATABLOCK ClassQueryWmiDataBlock;
    PCLASS_SET_WMI_DATABLOCK ClassSetWmiDataBlock;
    PCLASS_SET_WMI_DATAITEM ClassSetWmiDataItem;
    PCLASS_EXECUTE_WMI_METHOD ClassExecuteWmiMethod;
    PCLASS_WMI_FUNCTION_CONTROL ClassWmiFunctionControl;
} CLASS_WMI_INFO, * PCLASS_WMI_INFO;

typedef struct _CLASS_DEV_INFO {
    ULONG DeviceExtensionSize;
    DEVICE_TYPE DeviceType;
    UCHAR StackSize;
    ULONG DeviceCharacteristics;
    PCLASS_ERROR ClassError;
    PCLASS_READ_WRITE ClassReadWriteVerification;
    PCLASS_DEVICE_CONTROL ClassDeviceControl;
    PCLASS_SHUTDOWN_FLUSH ClassShutdownFlush;
    PCLASS_CREATE_CLOSE ClassCreateClose;
    PCLASS_INIT_DEVICE ClassInitDevice;
    PCLASS_START_DEVICE ClassStartDevice;
    PCLASS_POWER_DEVICE ClassPowerDevice;
    PCLASS_STOP_DEVICE ClassStopDevice;
    PCLASS_REMOVE_DEVICE ClassRemoveDevice;
    PCLASS_QUERY_PNP_CAPABILITIES ClassQueryPnpCapabilities;
    CLASS_WMI_INFO ClassWmiInfo;
} CLASS_DEV_INFO, * PCLASS_DEV_INFO;

struct _CLASS_INIT_DATA {
    ULONG InitializationDataSize;
    CLASS_DEV_INFO FdoData;
    CLASS_DEV_INFO PdoData;
    PCLASS_ADD_DEVICE ClassAddDevice;
    PCLASS_ENUM_DEVICE ClassEnumerateDevice;
    PCLASS_QUERY_ID ClassQueryId;
    PDRIVER_STARTIO ClassStartIo;
    PCLASS_UNLOAD ClassUnload;
    PCLASS_TICK ClassTick;
};

typedef struct _FILE_OBJECT_EXTENSION {
    PFILE_OBJECT FileObject;
    PDEVICE_OBJECT DeviceObject;
    ULONG LockCount;
    ULONG McnDisableCount;
} FILE_OBJECT_EXTENSION, * PFILE_OBJECT_EXTENSION;

typedef struct _CLASS_WORKING_SET {
    _Field_range_(sizeof(CLASS_WORKING_SET), sizeof(CLASS_WORKING_SET)) ULONG Size;
    _Field_range_(0, 2048) ULONG XferPacketsWorkingSetMaximum;
    _Field_range_(0, 2048) ULONG XferPacketsWorkingSetMinimum;
} CLASS_WORKING_SET, * PCLASS_WORKING_SET;

typedef struct _CLASS_INTERPRET_SENSE_INFO2 {
    _Field_range_(sizeof(CLASS_INTERPRET_SENSE_INFO), sizeof(CLASS_INTERPRET_SENSE_INFO))
        ULONG Size;
    _Field_range_(1, 30000) ULONG HistoryCount;
    __callback PCLASS_COMPRESS_RETRY_HISTORY_DATA Compress;
    __callback PCLASS_INTERPRET_SENSE_INFO Interpret;
} CLASS_INTERPRET_SENSE_INFO2, * PCLASS_INTERPRET_SENSE_INFO2;

C_ASSERT((MAXULONG - sizeof(SRB_HISTORY)) / 30000 >= sizeof(SRB_HISTORY_ITEM));

// for SrbSupport
#define CLASS_SRB_SCSI_REQUEST_BLOCK    0x1
#define CLASS_SRB_STORAGE_REQUEST_BLOCK 0x2

typedef struct _CLASS_DRIVER_EXTENSION {
    UNICODE_STRING RegistryPath;
    CLASS_INIT_DATA InitData;
    ULONG DeviceCount;
#if (NTDDI_VERSION >= NTDDI_WINXP)
    PCLASS_QUERY_WMI_REGINFO_EX ClassFdoQueryWmiRegInfoEx;
    PCLASS_QUERY_WMI_REGINFO_EX ClassPdoQueryWmiRegInfoEx;
#endif
#if (NTDDI_VERSION >= NTDDI_VISTA)
    REGHANDLE EtwHandle;
    PDRIVER_DISPATCH DeviceMajorFunctionTable[IRP_MJ_MAXIMUM_FUNCTION + 1];
    PDRIVER_DISPATCH MpDeviceMajorFunctionTable[IRP_MJ_MAXIMUM_FUNCTION + 1];
    PCLASS_INTERPRET_SENSE_INFO2 InterpretSenseInfo;
    PCLASS_WORKING_SET WorkingSet;
#endif
#if (NTDDI_VERSION >= NTDDI_WIN8)
    ULONG SrbSupport;
#endif
} CLASS_DRIVER_EXTENSION, * PCLASS_DRIVER_EXTENSION;

#pragma warning (disable:4201)
typedef struct _COMMON_DEVICE_EXTENSION {
    ULONG Version;
    PDEVICE_OBJECT DeviceObject;
    PDEVICE_OBJECT LowerDeviceObject;
    struct _FUNCTIONAL_DEVICE_EXTENSION* PartitionZeroExtension;
    PCLASS_DRIVER_EXTENSION DriverExtension;
    LONG RemoveLock;
    KEVENT RemoveEvent;
    KSPIN_LOCK RemoveTrackingSpinlock;
    PVOID RemoveTrackingList;
    LONG RemoveTrackingUntrackedCount;
    PVOID DriverData;
    struct {
        BOOLEAN IsFdo : 1;
        BOOLEAN IsInitialized : 1;
        BOOLEAN IsSrbLookasideListInitialized : 1;
    } DUMMYSTRUCTNAME;
    UCHAR PreviousState;
    UCHAR CurrentState;
    ULONG IsRemoved;
    UNICODE_STRING DeviceName;
    struct _PHYSICAL_DEVICE_EXTENSION* ChildList;
    ULONG PartitionNumber;
    LARGE_INTEGER PartitionLength;
    LARGE_INTEGER StartingOffset;
    PCLASS_DEV_INFO DevInfo;
    ULONG PagingPathCount;
    ULONG DumpPathCount;
    ULONG HibernationPathCount;
    KEVENT PathCountEvent;
#ifndef ALLOCATE_SRB_FROM_POOL
    NPAGED_LOOKASIDE_LIST SrbLookasideList;
#endif
    UNICODE_STRING MountedDeviceInterfaceName;
    ULONG GuidCount;
    PGUIDREGINFO GuidRegInfo;
    DICTIONARY FileObjectDictionary;
#if (NTDDI_VERSION >= NTDDI_WINXP)
    PCLASS_PRIVATE_COMMON_DATA PrivateCommonData;
#else
    ULONG_PTR Reserved1;
#endif
#if (NTDDI_VERSION >= NTDDI_VISTA)
    PDRIVER_DISPATCH* DispatchTable;
#else
    ULONG_PTR Reserved2;
#endif
    ULONG_PTR Reserved3;
    ULONG_PTR Reserved4;
} COMMON_DEVICE_EXTENSION, * PCOMMON_DEVICE_EXTENSION;

typedef struct _PHYSICAL_DEVICE_EXTENSION {
    union {
        struct {
            ULONG Version;
            PDEVICE_OBJECT DeviceObject;
        } DUMMYSTRUCTNAME;
        COMMON_DEVICE_EXTENSION CommonExtension;
    } DUMMYUNIONNAME;
    BOOLEAN IsMissing;
    BOOLEAN IsEnumerated;
#if (NTDDI_VERSION >= NTDDI_WINXP)
    PCLASS_PRIVATE_PDO_DATA PrivatePdoData;
#else
    ULONG_PTR Reserved1;
#endif
    ULONG_PTR Reserved2;
    ULONG_PTR Reserved3;
    ULONG_PTR Reserved4;
} PHYSICAL_DEVICE_EXTENSION, * PPHYSICAL_DEVICE_EXTENSION;

typedef struct _CLASS_POWER_OPTIONS {
    ULONG PowerDown : 1;
    ULONG LockQueue : 1;
    ULONG HandleSpinDown : 1;
    ULONG HandleSpinUp : 1;
    ULONG Reserved : 27;
} CLASS_POWER_OPTIONS, * PCLASS_POWER_OPTIONS;

typedef struct _CLASS_POWER_CONTEXT {
    union {
        CLASS_POWER_DOWN_STATE PowerDown;
        CLASS_POWER_DOWN_STATE2 PowerDown2;
        CLASS_POWER_DOWN_STATE3 PowerDown3;
        CLASS_POWER_UP_STATE PowerUp;
    } PowerChangeState;
    CLASS_POWER_OPTIONS Options;
    BOOLEAN InUse;
    BOOLEAN QueueLocked;
    NTSTATUS FinalStatus;
    ULONG RetryCount;
    ULONG RetryInterval;
    PIO_COMPLETION_ROUTINE CompletionRoutine;
    PDEVICE_OBJECT DeviceObject;
    PIRP Irp;
    SCSI_REQUEST_BLOCK Srb;
} CLASS_POWER_CONTEXT, * PCLASS_POWER_CONTEXT;

#if (NTDDI_VERSION >= NTDDI_WIN8)

#define CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE (sizeof(STORAGE_REQUEST_BLOCK) + sizeof(STOR_ADDR_BTL8) + sizeof(SRBEX_DATA_SCSI_CDB16))
#define CLASS_SRBEX_NO_SRBEX_DATA_BUFFER_SIZE (sizeof(STORAGE_REQUEST_BLOCK) + sizeof(STOR_ADDR_BTL8))

#endif

typedef struct _COMPLETION_CONTEXT {
    PDEVICE_OBJECT DeviceObject;
#if (NTDDI_VERSION >= NTDDI_WIN8)
    union
    {
        SCSI_REQUEST_BLOCK Srb;
        STORAGE_REQUEST_BLOCK SrbEx;
        UCHAR SrbExBuffer[CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE];
    } Srb;
#else
    SCSI_REQUEST_BLOCK Srb;
#endif
} COMPLETION_CONTEXT, * PCOMPLETION_CONTEXT;

_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
SCSIPORT_API
ULONG
NTAPI
ClassInitialize(
    _In_ PVOID Argument1,
    _In_ PVOID Argument2,
    _In_ PCLASS_INIT_DATA InitializationData);

typedef struct _CLASS_QUERY_WMI_REGINFO_EX_LIST {
    ULONG Size;
    __callback PCLASS_QUERY_WMI_REGINFO_EX ClassFdoQueryWmiRegInfoEx;
    __callback PCLASS_QUERY_WMI_REGINFO_EX ClassPdoQueryWmiRegInfoEx;
} CLASS_QUERY_WMI_REGINFO_EX_LIST, * PCLASS_QUERY_WMI_REGINFO_EX_LIST;

typedef enum
{
    SupportUnknown = 0,
    Supported,
    NotSupported
} CLASS_FUNCTION_SUPPORT;

typedef struct _CLASS_VPD_B1_DATA
{
    NTSTATUS CommandStatus;
    USHORT MediumRotationRate;
    UCHAR NominalFormFactor;
    UCHAR Zoned;
    ULONG MediumProductType;
    ULONG DepopulationTime;
} CLASS_VPD_B1_DATA, * PCLASS_VPD_B1_DATA;

typedef struct _CLASS_VPD_B0_DATA
{
    NTSTATUS CommandStatus;
    ULONG MaxUnmapLbaCount;
    ULONG MaxUnmapBlockDescrCount;
    ULONG OptimalUnmapGranularity;
    ULONG UnmapGranularityAlignment;
    BOOLEAN UGAVALID;
    UCHAR Reserved0;
    USHORT OptimalTransferLengthGranularity;
    ULONG MaximumTransferLength;
    ULONG OptimalTransferLength;
} CLASS_VPD_B0_DATA, * PCLASS_VPD_B0_DATA;

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4214)
#endif
typedef struct _CLASS_VPD_B2_DATA
{
    NTSTATUS CommandStatus;
    UCHAR ThresholdExponent;
    UCHAR DP : 1;
    UCHAR ANC_SUP : 1;
    UCHAR Reserved0 : 2;
    UCHAR LBPRZ : 1;
    UCHAR LBPWS10 : 1;
    UCHAR LBPWS : 1;
    UCHAR LBPU : 1;
    UCHAR ProvisioningType : 3;
    UCHAR Reserved1 : 5;
    ULONG SoftThresholdEventPending;
} CLASS_VPD_B2_DATA, * PCLASS_VPD_B2_DATA;
#ifdef _MSC_VER
#pragma warning(pop)
#endif

typedef struct _CLASS_READ_CAPACITY16_DATA
{
    NTSTATUS CommandStatus;
    ULONG BytesPerLogicalSector;
    ULONG BytesPerPhysicalSector;
    ULONG BytesOffsetForSectorAlignment;
    BOOLEAN LBProvisioningEnabled;
    BOOLEAN LBProvisioningReadZeros;
    UCHAR Reserved0[2];
    ULONG Reserved1;
} CLASS_READ_CAPACITY16_DATA, * PCLASS_READ_CAPACITY16_DATA;

typedef struct _CLASS_VPD_ECOP_BLOCK_DEVICE_ROD_LIMITS
{
    NTSTATUS CommandStatus;
    USHORT MaximumRangeDescriptors;
    UCHAR Restricted;
    UCHAR Reserved;
    ULONG MaximumInactivityTimer;
    ULONG DefaultInactivityTimer;
    ULONGLONG MaximumTokenTransferSize;
    ULONGLONG OptimalTransferCount;
} CLASS_VPD_ECOP_BLOCK_DEVICE_ROD_LIMITS, * PCLASS_VPD_ECOP_BLOCK_DEVICE_ROD_LIMITS;

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4214)
#endif
typedef struct _CLASS_FUNCTION_SUPPORT_INFO
{
    KSPIN_LOCK SyncLock;
    ULONG GenerationCount;
    volatile ULONG ChangeRequestCount;
    struct
    {
        ULONG BlockLimits : 1;
        ULONG BlockDeviceCharacteristics : 1;
        ULONG LBProvisioning : 1;
        ULONG BlockDeviceRODLimits : 1;
        ULONG ZonedBlockDeviceCharacteristics : 1;
        ULONG Reserved : 22;
        ULONG DeviceType : 5;
    } ValidInquiryPages;
    struct
    {
        CLASS_FUNCTION_SUPPORT SeekPenaltyProperty;
        CLASS_FUNCTION_SUPPORT AccessAlignmentProperty;
        CLASS_FUNCTION_SUPPORT TrimProperty;
        CLASS_FUNCTION_SUPPORT TrimProcess;
    } LowerLayerSupport;
    BOOLEAN RegAccessAlignmentQueryNotSupported;
    BOOLEAN AsynchronousNotificationSupported;
#if (NTDDI_VERSION >= NTDDI_WIN10_RS2)
    BOOLEAN UseModeSense10;
    UCHAR Reserved;
#else
    UCHAR Reserved[2];
#endif
    CLASS_VPD_B0_DATA BlockLimitsData;
    CLASS_VPD_B1_DATA DeviceCharacteristicsData;
    CLASS_VPD_B2_DATA LBProvisioningData;
    CLASS_READ_CAPACITY16_DATA ReadCapacity16Data;
    CLASS_VPD_ECOP_BLOCK_DEVICE_ROD_LIMITS BlockDeviceRODLimitsData;
    struct
    {
        ULONG D3ColdSupported : 1;
        ULONG DeviceWakeable : 1;
        ULONG IdlePowerEnabled : 1;
        ULONG D3IdleTimeoutOverridden : 1;
        ULONG NoVerifyDuringIdlePower : 1;
        ULONG Reserved2 : 27;
        ULONG D3IdleTimeout;
    } IdlePower;

#if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
    CLASS_FUNCTION_SUPPORT HwFirmwareGetInfoSupport;
    PSTORAGE_HW_FIRMWARE_INFO HwFirmwareInfo;
#endif
} CLASS_FUNCTION_SUPPORT_INFO, * PCLASS_FUNCTION_SUPPORT_INFO;
#ifdef _MSC_VER
#pragma warning(pop)
#endif

typedef struct _FUNCTIONAL_DEVICE_EXTENSION {
    union {
        struct {
            ULONG Version;
            PDEVICE_OBJECT DeviceObject;
        } DUMMYSTRCTNAME;
        COMMON_DEVICE_EXTENSION CommonExtension;
    } DUMMYUNIONNAME;
    PDEVICE_OBJECT LowerPdo;
    PSTORAGE_DEVICE_DESCRIPTOR DeviceDescriptor;
    PSTORAGE_ADAPTER_DESCRIPTOR AdapterDescriptor;
    DEVICE_POWER_STATE DevicePowerState;
    ULONG DMByteSkew;
    ULONG DMSkew;
    BOOLEAN DMActive;
#if (NTDDI_VERSION >= NTDDI_WIN8)
    UCHAR SenseDataLength;
#else
    UCHAR Reserved;
#endif
    UCHAR Reserved0[2];
    DISK_GEOMETRY DiskGeometry;
    PSENSE_DATA SenseData;
    ULONG TimeOutValue;
    ULONG DeviceNumber;
    ULONG SrbFlags;
    ULONG ErrorCount;
    LONG LockCount;
    LONG ProtectedLockCount;
    LONG InternalLockCount;
    KEVENT EjectSynchronizationEvent;
    USHORT DeviceFlags;
    UCHAR SectorShift;
#if (NTDDI_VERSION >= NTDDI_VISTA)
    UCHAR CdbForceUnitAccess;
#else
    UCHAR ReservedByte;
#endif
    PMEDIA_CHANGE_DETECTION_INFO MediaChangeDetectionInfo;
    PKEVENT Unused1;
    HANDLE Unused2;
    FILE_OBJECT_EXTENSION KernelModeMcnContext;
    ULONG MediaChangeCount;
    HANDLE DeviceDirectory;
    KSPIN_LOCK ReleaseQueueSpinLock;
    PIRP ReleaseQueueIrp;
    SCSI_REQUEST_BLOCK ReleaseQueueSrb;
    BOOLEAN ReleaseQueueNeeded;
    BOOLEAN ReleaseQueueInProgress;
    BOOLEAN ReleaseQueueIrpFromPool;
    BOOLEAN FailurePredicted;
    ULONG FailureReason;
    struct _FAILURE_PREDICTION_INFO* FailurePredictionInfo;
    BOOLEAN PowerDownInProgress;
    ULONG EnumerationInterlock;
    KEVENT ChildLock;
    PKTHREAD ChildLockOwner;
    ULONG ChildLockAcquisitionCount;
    ULONG ScanForSpecialFlags;
    KDPC PowerRetryDpc;
    KTIMER PowerRetryTimer;
    CLASS_POWER_CONTEXT PowerContext;

#if (NTDDI_VERSION <= NTDDI_WIN2K)

#if (SPVER(NTDDI_VERSION) < 2))
    ULONG_PTR Reserved1;
    ULONG_PTR Reserved2;
    ULONG_PTR Reserved3;
    ULONG_PTR Reserved4;
#else
    ULONG CompletionSuccessCount;
    ULONG SavedSrbFlags;
    ULONG SavedErrorCount;
    ULONG_PTR Reserved1;
#endif /* (SPVER(NTDDI_VERSION) < 2) */

#else /* (NTDDI_VERSION <= NTDDI_WIN2K) */

    PCLASS_PRIVATE_FDO_DATA PrivateFdoData;
#if (NTDDI_VERSION >= NTDDI_WIN8)
    PCLASS_FUNCTION_SUPPORT_INFO FunctionSupportInfo;
    PSTORAGE_MINIPORT_DESCRIPTOR MiniportDescriptor;
#else
    ULONG_PTR Reserved2;
    ULONG_PTR Reserved3;
#endif

#if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
    PVOID AdditionalFdoData;
#else
    ULONG_PTR Reserved4;
#endif
#endif /* (NTDDI_VERSION <= NTDDI_WIN2K) */

} FUNCTIONAL_DEVICE_EXTENSION, * PFUNCTIONAL_DEVICE_EXTENSION;

_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
SCSIPORT_API
ULONG
NTAPI
ClassInitializeEx(
    _In_ PDRIVER_OBJECT DriverObject,
    _In_ LPGUID Guid,
    _In_ PVOID Data);

_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
_Post_satisfies_(return <= 0)
SCSIPORT_API
NTSTATUS
NTAPI
ClassCreateDeviceObject(
    _In_ PDRIVER_OBJECT DriverObject,
    _In_z_ PCCHAR ObjectNameBuffer,
    _In_ PDEVICE_OBJECT LowerDeviceObject,
    _In_ BOOLEAN IsFdo,
    _Outptr_result_nullonfailure_ _At_(*DeviceObject, __drv_allocatesMem(Mem) __drv_aliasesMem)
    PDEVICE_OBJECT * DeviceObject);

_Must_inspect_result_
SCSIPORT_API
NTSTATUS
NTAPI
ClassReadDriveCapacity(
    _In_ PDEVICE_OBJECT DeviceObject);

SCSIPORT_API
VOID
NTAPI
ClassReleaseQueue(
    _In_ PDEVICE_OBJECT DeviceObject);

SCSIPORT_API
VOID
NTAPI
ClassSplitRequest(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp,
    _In_ ULONG MaximumBytes);

SCSIPORT_API
NTSTATUS
NTAPI
ClassDeviceControl(
    _In_ PDEVICE_OBJECT DeviceObject,
    _Inout_ PIRP Irp);

SCSIPORT_API
NTSTATUS
NTAPI
ClassIoComplete(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp,
    PVOID Context);

SCSIPORT_API
NTSTATUS
NTAPI
ClassIoCompleteAssociated(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp,
    PVOID Context);

SCSIPORT_API
BOOLEAN
NTAPI
ClassInterpretSenseInfo(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PSCSI_REQUEST_BLOCK Srb,
    _In_ UCHAR MajorFunctionCode,
    _In_ ULONG IoDeviceCode,
    _In_ ULONG RetryCount,
    _Out_ NTSTATUS * Status,
    _Out_opt_ _Deref_out_range_(0, 100) ULONG * RetryInterval);

VOID
NTAPI
ClassSendDeviceIoControlSynchronous(
    _In_ ULONG IoControlCode,
    _In_ PDEVICE_OBJECT TargetDeviceObject,
    _Inout_updates_opt_(_Inexpressible_(max(InputBufferLength, OutputBufferLength)))
    PVOID Buffer,
    _In_ ULONG InputBufferLength,
    _In_ ULONG OutputBufferLength,
    _In_ BOOLEAN InternalDeviceIoControl,
    _Out_ PIO_STATUS_BLOCK IoStatus);

SCSIPORT_API
NTSTATUS
NTAPI
ClassSendIrpSynchronous(
    _In_ PDEVICE_OBJECT TargetDeviceObject,
    _In_ PIRP Irp);

SCSIPORT_API
NTSTATUS
NTAPI
ClassForwardIrpSynchronous(
    _In_ PCOMMON_DEVICE_EXTENSION CommonExtension,
    _In_ PIRP Irp);

SCSIPORT_API
NTSTATUS
NTAPI
ClassSendSrbSynchronous(
    _In_ PDEVICE_OBJECT DeviceObject,
    _Inout_ PSCSI_REQUEST_BLOCK Srb,
    _In_reads_bytes_opt_(BufferLength) PVOID BufferAddress,
    _In_ ULONG BufferLength,
    _In_ BOOLEAN WriteToDevice);

SCSIPORT_API
NTSTATUS
NTAPI
ClassSendSrbAsynchronous(
    _In_ PDEVICE_OBJECT DeviceObject,
    _Inout_ PSCSI_REQUEST_BLOCK Srb,
    _In_ PIRP Irp,
    _In_reads_bytes_opt_(BufferLength) __drv_aliasesMem PVOID BufferAddress,
    _In_ ULONG BufferLength,
    _In_ BOOLEAN WriteToDevice);

SCSIPORT_API
NTSTATUS
NTAPI
ClassBuildRequest(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp);

SCSIPORT_API
ULONG
NTAPI
ClassModeSense(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_reads_bytes_(Length) PCHAR ModeSenseBuffer,
    _In_ ULONG Length,
    _In_ UCHAR PageMode);

SCSIPORT_API
PVOID
NTAPI
ClassFindModePage(
    _In_reads_bytes_(Length) PCHAR ModeSenseBuffer,
    _In_ ULONG Length,
    _In_ UCHAR PageMode,
    _In_ BOOLEAN Use6Byte);

_IRQL_requires_max_(PASSIVE_LEVEL)
SCSIPORT_API
NTSTATUS
NTAPI
ClassClaimDevice(
    _In_ PDEVICE_OBJECT LowerDeviceObject,
    _In_ BOOLEAN Release);

SCSIPORT_API
NTSTATUS
NTAPI
ClassInternalIoControl(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp);

_IRQL_requires_max_(PASSIVE_LEVEL)
SCSIPORT_API
VOID
NTAPI
ClassInitializeSrbLookasideList(
    _Inout_ PCOMMON_DEVICE_EXTENSION CommonExtension,
    _In_ ULONG NumberElements);

_IRQL_requires_max_(PASSIVE_LEVEL)
SCSIPORT_API
VOID
NTAPI
ClassDeleteSrbLookasideList(
    _Inout_ PCOMMON_DEVICE_EXTENSION CommonExtension);

_IRQL_requires_max_(PASSIVE_LEVEL)
SCSIPORT_API
ULONG
NTAPI
ClassQueryTimeOutRegistryValue(
    _In_ PDEVICE_OBJECT DeviceObject);

_IRQL_requires_max_(PASSIVE_LEVEL)
SCSIPORT_API
NTSTATUS
NTAPI
ClassGetDescriptor(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PSTORAGE_PROPERTY_ID PropertyId,
    _Outptr_ PVOID * Descriptor);

_IRQL_requires_max_(PASSIVE_LEVEL)
SCSIPORT_API
VOID
NTAPI
ClassInvalidateBusRelations(
    _In_ PDEVICE_OBJECT Fdo);

_IRQL_requires_max_(PASSIVE_LEVEL)
SCSIPORT_API
VOID
NTAPI
ClassMarkChildrenMissing(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION Fdo);

_IRQL_requires_max_(PASSIVE_LEVEL)
SCSIPORT_API
BOOLEAN
NTAPI
ClassMarkChildMissing(
    _In_ PPHYSICAL_DEVICE_EXTENSION PdoExtension,
    _In_ BOOLEAN AcquireChildLock);

SCSIPORT_API
VOID
ClassDebugPrint(
    _In_ CLASS_DEBUG_LEVEL DebugPrintLevel,
    _In_z_ PCCHAR DebugMessage,
    ...);

__drv_aliasesMem
_IRQL_requires_max_(DISPATCH_LEVEL)
SCSIPORT_API
PCLASS_DRIVER_EXTENSION
NTAPI
ClassGetDriverExtension(
    _In_ PDRIVER_OBJECT DriverObject);

SCSIPORT_API
VOID
NTAPI
ClassCompleteRequest(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp,
    _In_ CCHAR PriorityBoost);

SCSIPORT_API
VOID
NTAPI
ClassReleaseRemoveLock(
    _In_ PDEVICE_OBJECT DeviceObject,
    PIRP Tag);

SCSIPORT_API
ULONG
NTAPI
ClassAcquireRemoveLockEx(
    _In_ PDEVICE_OBJECT DeviceObject,
    PVOID Tag,
    _In_ PCSTR File,
    _In_ ULONG Line);

_IRQL_requires_max_(PASSIVE_LEVEL)
SCSIPORT_API
VOID
NTAPI
ClassUpdateInformationInRegistry(
    _In_ PDEVICE_OBJECT Fdo,
    _In_ PCHAR DeviceName,
    _In_ ULONG DeviceNumber,
    _In_reads_bytes_opt_(InquiryDataLength) PINQUIRYDATA InquiryData,
    _In_ ULONG InquiryDataLength);

SCSIPORT_API
NTSTATUS
NTAPI
ClassWmiCompleteRequest(
    _In_ PDEVICE_OBJECT DeviceObject,
    _Inout_ PIRP Irp,
    _In_ NTSTATUS Status,
    _In_ ULONG BufferUsed,
    _In_ CCHAR PriorityBoost);

_IRQL_requires_max_(DISPATCH_LEVEL)
SCSIPORT_API
NTSTATUS
NTAPI
ClassWmiFireEvent(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ LPGUID Guid,
    _In_ ULONG InstanceIndex,
    _In_ ULONG EventDataSize,
    _In_reads_bytes_(EventDataSize) PVOID EventData);

SCSIPORT_API
VOID
NTAPI
ClassResetMediaChangeTimer(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension);

_IRQL_requires_max_(PASSIVE_LEVEL)
SCSIPORT_API
VOID
NTAPI
ClassInitializeMediaChangeDetection(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_ PUCHAR EventPrefix);

_IRQL_requires_max_(PASSIVE_LEVEL)
SCSIPORT_API
NTSTATUS
NTAPI
ClassInitializeTestUnitPolling(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_ BOOLEAN AllowDriveToSleep);

SCSIPORT_API
PVPB
NTAPI
ClassGetVpb(
    _In_ PDEVICE_OBJECT DeviceObject);

SCSIPORT_API
NTSTATUS
NTAPI
ClassSpinDownPowerHandler(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp);

NTSTATUS
NTAPI
ClassStopUnitPowerHandler(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp);

_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
NTAPI
ClassSetFailurePredictionPoll(
    _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_ FAILURE_PREDICTION_METHOD FailurePredictionMethod,
    _In_ ULONG PollingPeriod);

_IRQL_requires_max_(DISPATCH_LEVEL)
VOID
NTAPI
ClassNotifyFailurePredicted(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_reads_bytes_(BufferSize) PUCHAR Buffer,
    _In_ ULONG BufferSize,
    _In_ BOOLEAN LogError,
    _In_ ULONG UniqueErrorValue,
    _In_ UCHAR PathId,
    _In_ UCHAR TargetId,
    _In_ UCHAR Lun);

_IRQL_requires_max_(PASSIVE_LEVEL)
SCSIPORT_API
VOID
NTAPI
ClassAcquireChildLock(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension);

SCSIPORT_API
VOID
NTAPI
ClassReleaseChildLock(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension);

IO_COMPLETION_ROUTINE ClassSignalCompletion;

VOID
NTAPI
ClassSendStartUnit(
    _In_ PDEVICE_OBJECT DeviceObject);

_IRQL_requires_max_(PASSIVE_LEVEL)
SCSIPORT_API
NTSTATUS
NTAPI
ClassRemoveDevice(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ UCHAR RemoveType);

SCSIPORT_API
NTSTATUS
NTAPI
ClassAsynchronousCompletion(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp,
    PVOID Event);

SCSIPORT_API
VOID
NTAPI
ClassCheckMediaState(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension);

SCSIPORT_API
NTSTATUS
NTAPI
ClassCheckVerifyComplete(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp,
    PVOID Context);

_IRQL_requires_max_(PASSIVE_LEVEL)
SCSIPORT_API
VOID
NTAPI
ClassSetMediaChangeState(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_ MEDIA_CHANGE_DETECTION_STATE State,
    _In_ BOOLEAN Wait);

_IRQL_requires_max_(PASSIVE_LEVEL)
SCSIPORT_API
VOID
NTAPI
ClassEnableMediaChangeDetection(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension);

_IRQL_requires_max_(PASSIVE_LEVEL)
SCSIPORT_API
VOID
NTAPI
ClassDisableMediaChangeDetection(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension);

_IRQL_requires_max_(PASSIVE_LEVEL)
SCSIPORT_API
VOID
NTAPI
ClassCleanupMediaChangeDetection(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension);

_IRQL_requires_max_(PASSIVE_LEVEL)
VOID
NTAPI
ClassGetDeviceParameter(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_opt_ PWSTR SubkeyName,
    _In_ PWSTR ParameterName,
    _Inout_ PULONG ParameterValue);

_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
NTAPI
ClassSetDeviceParameter(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_opt_ PWSTR SubkeyName,
    _In_ PWSTR ParameterName,
    _In_ ULONG ParameterValue);

#if (NTDDI_VERSION >= NTDDI_VISTA)

_IRQL_requires_max_(PASSIVE_LEVEL)
PFILE_OBJECT_EXTENSION
NTAPI
ClassGetFsContext(
    _In_ PCOMMON_DEVICE_EXTENSION CommonExtension,
    _In_ PFILE_OBJECT FileObject);

_IRQL_requires_max_(DISPATCH_LEVEL)
VOID
NTAPI
ClassSendNotification(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_ const GUID * Guid,
    _In_ ULONG ExtraDataSize,
    _In_reads_bytes_opt_(ExtraDataSize) PVOID ExtraData);

#endif /* (NTDDI_VERSION >= NTDDI_VISTA) */

FORCEINLINE
UCHAR
GET_FDO_EXTENSON_SENSE_DATA_LENGTH(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension)
{
    UCHAR SenseDataLength = 0;

    if (FdoExtension->SenseData != NULL)
    {
#if (NTDDI_VERSION >= NTDDI_WIN8)
        if (FdoExtension->SenseDataLength > 0)
        {
            SenseDataLength = FdoExtension->SenseDataLength;
        }
        else
        {
            // For backward compatibility with Windows 7 and earlier
            SenseDataLength = SENSE_BUFFER_SIZE;
        }
#else
        SenseDataLength = SENSE_BUFFER_SIZE;
#endif
    }

    return SenseDataLength;
}

static __inline
BOOLEAN
PORT_ALLOCATED_SENSE(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_ PSCSI_REQUEST_BLOCK Srb)
{
    return ((BOOLEAN)((TEST_FLAG(Srb->SrbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE) &&
        TEST_FLAG(Srb->SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER)) &&
        (Srb->SenseInfoBuffer != FdoExtension->SenseData)));
}

static __inline
VOID
FREE_PORT_ALLOCATED_SENSE_BUFFER(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_ PSCSI_REQUEST_BLOCK Srb)
{
    ASSERT(TEST_FLAG(Srb->SrbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
    ASSERT(TEST_FLAG(Srb->SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
    ASSERT(Srb->SenseInfoBuffer != FdoExtension->SenseData);

    ExFreePool(Srb->SenseInfoBuffer);
    Srb->SenseInfoBuffer = FdoExtension->SenseData;
    Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
    CLEAR_FLAG(Srb->SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER);
    return;
}

_IRQL_requires_max_(PASSIVE_LEVEL)
typedef VOID
(NTAPI* PCLASS_SCAN_FOR_SPECIAL_HANDLER)(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_ ULONG_PTR Data);

_IRQL_requires_max_(PASSIVE_LEVEL)
VOID
NTAPI
ClassScanForSpecial(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_ CLASSPNP_SCAN_FOR_SPECIAL_INFO DeviceList[],
    _In_ PCLASS_SCAN_FOR_SPECIAL_HANDLER Function);